import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
from pymongo import MongoClient
import json
from bson.objectid import ObjectId

# 连接到 MongoDB
client = MongoClient('mongodb://localhost:27017/')  # 根据实际情况修改
db = client['test']
collection = db['review']
book_collection = db['book']

# 从 MongoDB 中获取数据
data = []
for doc in collection.find():
    data.append({
        'u_id': str(doc['u_id']),  # 确保u_id是字符串类型
        'b_id': str(doc['b_id']),  # 确保b_id是字符串类型
        'score': doc['rating']
    })

# 将数据转换为 DataFrame
df = pd.DataFrame(data)

# 预处理：提取评分数据
ratings = df[df['score'].notnull()][['u_id', 'b_id', 'score']]

# 创建用户-物品矩阵
user_item_matrix = ratings.pivot_table(index='u_id', columns='b_id', values='score', fill_value=0)

# 计算用户相似度
user_similarity = cosine_similarity(user_item_matrix)
# 将相似度矩阵转换为DataFrame，方便后续操作
user_similarity_df = pd.DataFrame(user_similarity, index=user_item_matrix.index, columns=user_item_matrix.index)


# 获取相似用户
def get_similar_users(user_id, n=10):
    # 获取与指定用户的相似度
    similar_scores = user_similarity_df[user_id]
    # 按相似度降序排序
    similar_scores = similar_scores.sort_values(ascending=False)
    # 排除用户自身
    similar_scores = similar_scores[similar_scores.index != user_id]
    return similar_scores.head(n)  # 只返回前n个相似用户


# 获取候选书籍
def get_candidate_books(user_id, similar_users):
    candidate_books = set()
    # 遍历相似用户
    for user in similar_users.index:
        # 获取该用户评分过的书籍（评分>0的书籍）
        rated_books = user_item_matrix.loc[user][user_item_matrix.loc[user] > 0].index
        # 将这些书籍添加到候选书籍集合中
        candidate_books.update(rated_books)
    # 排除用户已经评分过的书籍
    user_rated_books = user_item_matrix.loc[user_id][user_item_matrix.loc[user_id] > 0].index
    candidate_books = candidate_books - set(user_rated_books)
    return candidate_books


# 预测评分
def predict_ratings(user_id, candidate_books, similar_users):
    predicted_ratings = {}
    for book in candidate_books:
        numerator = 0
        denominator = 0
        for similar_user in similar_users.index:
            if book in user_item_matrix.columns and user_item_matrix.loc[similar_user, book] > 0:
                # 计算分子（相似度乘以评分）
                numerator += similar_users[similar_user] * user_item_matrix.loc[similar_user, book]
                # 计算分母（相似度之和）
                denominator += similar_users[similar_user]
        if denominator != 0:
            # 计算预测评分
            predicted_ratings[book] = numerator / denominator
    return pd.Series(predicted_ratings)


# 推荐函数
def recommend_books(user_id, n=10):
    # 检查用户是否存在于矩阵中
    if user_id not in user_item_matrix.index:
        return json.dumps([])

    # 获取相似用户
    similar_users = get_similar_users(user_id)

    # 获取候选书籍
    candidate_books = get_candidate_books(user_id, similar_users)

    if not candidate_books:
        return json.dumps([])

    # 预测评分
    predicted_ratings = predict_ratings(user_id, candidate_books, similar_users)

    if predicted_ratings.empty:
        return json.dumps([])

    # 按评分排序并取前 n 个
    top_n_ratings = predicted_ratings.sort_values(ascending=False).head(n)

    result = []
    for book_id, score in top_n_ratings.items():
        result.append({
            'id': book_id,
            'score': float(score),
        })

    # 将结果转换为 JSON 字符串
    return json.dumps(result)


def guess_recommendations(user_id):
    try:
        # 确保user_id是字符串类型
        user_id = str(user_id)

        # 获取推荐的书籍id列表
        recommendations = recommend_books(user_id=user_id)
        recommended_books = json.loads(recommendations)

        results = []
        # 根据推荐的书籍id列表查询图书元数据
        for book in recommended_books:
            book_id = book['id']
            try:
                # 尝试将book_id转换为ObjectId类型
                object_id = ObjectId(book_id)
                book_data = book_collection.find_one({'_id': object_id})
                if book_data:
                    formatted_book = {
                        '_id': str(book_data['_id']),
                        'title': book_data.get('title', ''),
                        'author': book_data.get('author', ''),
                        'cover': book_data.get('cover', ''),
                        'predicted_score': book['score']
                    }
                    results.append(formatted_book)
            except Exception as e:
                print(f"转换ID为ObjectId时出错，ID: {book_id}, 错误信息: {e}")

        return results
    except Exception as e:
        print(f"推荐过程中出错: {e}")
        return []

